import type { CascaderOptions, CascaderOptionsData } from '../../types/options';
import { query, queryAll } from '../utils/query';
import { calculateElementWidth } from '../utils/dom';
import { getNextLevelData } from '../utils/data';

/**
 * 值管理器
 * 负责级联选择器的值渲染和转换逻辑
 */
export class ValueManager {
    private uuid: string;
    private splitStr: string;
    private options: CascaderOptions;
    private valValue: any[];

    constructor(uuid: string, splitStr: string, options: CascaderOptions, valValue: any[]) {
        this.uuid = uuid;
        this.splitStr = splitStr;
        this.options = options;
        this.valValue = valValue;
    }

    /**
     * 渲染值
     * @param value 要渲染的值
     */
    public renderValue(value: any[] = []) {
        const { displayRender, mode = "single", placeholder = "" } = this.options;

        const fullItemTagValue:string[][] = [];
        const checkedItemTagValue:string[][] = [];

        if (mode === "multiple") {
            const allItem = queryAll(".cascader-container_menus_menu_item", this.uuid) ?? [];
            for (let index = 0; index < allItem.length; index++) {
                const element = allItem[index];
                element?.classList.remove("cascader-container_menus_menu_item_half");
                element?.classList.remove("cascader-container_menus_menu_item_full");
            }

            for (let index = 0; index < this.valValue.length; index++) {
                const element = this.valValue[index];
                const item = query(`.cascader-container_menus_menu_item[data-tag="${element.join(this.splitStr)}"]`, this.uuid);
                item?.classList.add("cascader-container_menus_menu_item_full");

                for (let index = 0; index < element.length - 1; index++) {
                    const parentValue = element.slice(0, element.length - 1 - index);

                    if (checkedItemTagValue.map((ele) => ele.join(this.splitStr)).indexOf(parentValue.join(this.splitStr)) !== -1) {
                        continue;
                    }

                    const parentChild = getNextLevelData(parentValue, this.options);

                    const recursion = (data: CascaderOptionsData, tag: string[]) => {
                        const { value, children } = data ?? {};

                        if (children) {
                            let fullCount = 0;

                            for (let index = 0; index < children.length; index++) {
                                if (recursion(children[index], [...tag, String(value)])) {
                                    fullCount++;
                                }
                            }

                            return fullCount === children.length;
                        }

                        return this.valValue.findIndex((ele) => ele.join(this.splitStr) === `${tag.join(this.splitStr)}${this.splitStr}${value}`) !== -1;
                    };

                    const fullCount = parentChild.reduce((acc, cur) => {
                        if (recursion(cur, parentValue)) {
                            return acc + 1;
                        }

                        return acc;
                    }, 0);

                    const parentItem = query(`.cascader-container_menus_menu_item[data-tag="${parentValue.join(this.splitStr)}"]`, this.uuid);

                    if (fullCount === parentChild.length) {
                        parentItem?.classList.add("cascader-container_menus_menu_item_full");
                        fullItemTagValue.push(parentValue);
                    } else {
                        parentItem?.classList.add("cascader-container_menus_menu_item_half");
                    }

                    checkedItemTagValue.push(parentValue);
                }
            }
        }

        const valueDom = query(".cascader-container_value", this.uuid);

        if (valueDom) {
            if (value.length === 0) {
                valueDom.innerHTML = `<span class="cascader-container_value_placeholder">${placeholder}</span>`;

                return;
            }

            const labelValue = this.transformValue("getLabelValue", value);

            const getDisplayStr = (label: any[]) => {
                let displayStr;
                if (typeof displayRender === "function") {
                    displayStr = displayRender(label);
                } else {
                    displayStr = label.join("/");
                }

                return displayStr;
            };

            if (mode === "multiple") {
                const labelArr: any[] = [];

                for (let index = 0; index < value.length; index++) {
                    const element = value[index];
                    let label = labelValue[index];

                    if (fullItemTagValue.filter((ele) => ele.length === 1).find((ele) => element.join(this.splitStr).indexOf(ele.join("")) !== -1)) {
                        label = label.slice(0, 1);
                    }

                    if (labelArr.map((ele) => ele.join(this.splitStr)).indexOf(label.join("")) !== -1) {
                        continue;
                    }

                    labelArr.push(label);
                }

                let html = ``;
                const containerWidth = query(".cascader-container_value", this.uuid)?.offsetWidth ?? 0;
                let itemWidthTotal = 0;

                for (let index = 0; index < labelArr.length; index++) {
                    const element = labelArr[index];
                    const eleStr = `<div class="cascader-container_value_item">${getDisplayStr(element)}</div>`;
                    const itemWidth = calculateElementWidth(eleStr);
                    const afterItemWidthTotal = itemWidthTotal + itemWidth + 5;

                    if (afterItemWidthTotal <= containerWidth) {
                        html += eleStr;
                        itemWidthTotal = afterItemWidthTotal;
                    } else {
                        html += `<div class="cascader-container_value_item">...</div>`;
                        break;
                    }
                }

                valueDom.innerHTML = html;
            } else {
                valueDom.innerHTML = getDisplayStr(labelValue);
            }
        }
    }

    /**
     * 转换值
     * @param type 转换类型：getValue、getLabelValue、getIndexValue
     * @param value 要转换的值
     * @returns 转换后的结果
     */
    public transformValue(type: "getValue" | "getLabelValue" | "getIndexValue", value: any[] = []) {
        const { data = [], mode = "single" } = this.options;

        const getValue = (values: any[]) => {
            const result: any = [];

            let treeData: CascaderOptionsData[] = [...data];
            for (let index = 0; index < values.length; index++) {
                const element = values[index];

                if (type === "getValue") {
                    result.push(treeData[element]?.value);
                    treeData = treeData[element]?.children ?? [];
                } else if (type === "getLabelValue") {
                    const target = treeData.find((ele) => ele.value === element);
                    result.push(target?.label);
                    treeData = target?.children ?? [];
                } else if (type === "getIndexValue") {
                    result.push(treeData.findIndex((ele) => ele.value === element));
                    treeData = treeData.find((ele) => ele.value === element)?.children ?? [];
                }
            }

            return result;
        };

        if (mode === "multiple") {
            const result: any = [];

            for (let index = 0; index < value.length; index++) {
                const element = value[index];
                result.push(getValue(element));
            }

            return result;
        } else {
            return getValue(value);
        }
    }

    /**
     * 更新valValue
     * @param value 新的值
     */
    public updateValValue(value: any[]) {
        this.valValue = value;
    }
}